//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<tick_scheduler.cpp>
///	@path	~/src/engine/xcore
///	@date	2008/03/23
///	@desc	.

#include "config/config.h"

#include "lib/utilities/debug.h"
#include "lib/utilities/observer.h"
#include "lib/system/thread_debug.h"

#include "engine/xcore/tick_scheduler.h"

namespace xeres {

	// TickScheduler
	TickScheduler::TickScheduler( void )
		: m_reorderLoop( true )
	{
		ResetStages();
	}
	// ~TickScheduler
	TickScheduler::~TickScheduler( void )
	{

	}
	// TickFrame
	void TickScheduler::TickFrame( void )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("tick") );

		COUNTER_GUARD( _S("tick_frame") );
		// perform all calls
		for( size_t i = 0 , total = m_loopCalls.size() ; i < total ; ++i )
		{
			m_loopCalls[i]( *this );
		}

		// tailed completion
	}

	void TickScheduler::UpdateRootCall( void )
	{
		TRACK_FUNCTION_SCOPE();
		for( size_t i = 0 , total = m_loopRootCalls.size() ; i < total ; ++i )
		{
			m_loopRootCalls[i]( *this );
		}
	}

	void TickScheduler::UpdateSyncCall( void )
	{
		TRACK_FUNCTION_SCOPE();
		for( size_t i = 0 , total = m_loopSyncCalls.size() ; i < total ; ++i )
		{
			m_loopSyncCalls[i]( *this );
		}
	}

	// InsertStage
	bool TickScheduler::InsertStage( const WString& stage , const WString& dep , function<void(const Frame&)> exec_call )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|!pool*|!_render") );

		XS_ASSERT( !stage.empty() );

		WString mk_dep;
		if( stage[ stage.size() -1 ] != '.' && dep.find( '.' ) == WString::npos )
		{
			mk_dep = _S(".;") + dep;
		}
		else
		{
			mk_dep = dep;
		}

		// insert stage
		if( m_loopStages.insert_node( stage , mk_dep , exec_call ) )
		{
			m_reorderLoop = true;
			return true;
		}
		return false;

	}
	// DeleteStage
	bool TickScheduler::DeleteStage( const WString& stage )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|!pool*|!_render") );

		XS_ASSERT( !stage.empty() );

		if( m_loopStages.del_node( stage ) )
		{
			m_reorderLoop = true;
			return true;
		}
		return false;
	}
	// ReplaceStage
	bool TickScheduler::ReplaceStage( const WString& stage , function<void(const Frame&)> exec_call )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|!pool*|!_render") );

		XS_ASSERT( !stage.empty() );

		if( m_loopStages.replace_node( stage , exec_call ) )
		{
			m_reorderLoop = true;
			return true;
		}
		return false;
	}

	namespace
	{
		// game loop filter
		class _GameLoopFilter
		{
		public:
			//@ ctor
			_GameLoopFilter( std::vector< function<void(const Frame&)> >& gameCallback ,
				std::vector< function<void(const Frame&)> >& rootCallback ,
				std::vector< function<void(const Frame&)> >& syncCallback )
				: m_loop( gameCallback )
				, m_root( rootCallback )
				, m_sync( syncCallback )
			{
			}

			//@ ctor[copy]
			_GameLoopFilter( const _GameLoopFilter& rhs )
				: m_loop( rhs.m_loop )
				, m_root( rhs.m_root )
				, m_sync( rhs.m_sync )
			{
			}

			// nodes
			DepStagesFilterAction operator() ( const WString& path , const WString& name , function<void(const Frame&)> m_callback )
			{
				if( m_callback != NULL )
				{
					if( path == _S("$/") )
					{
						m_root.push_back( m_callback );
					}
					else if( path == _S("sync/") )
					{

						m_sync.push_back( m_callback );
					}
					else
					{
						m_loop.push_back( m_callback );
					}
				}
				return DEP_STAGES_ACTION_NEXT_NODE;
			}

		private:
		
		//@ data

			// game loop call
			std::vector< function<void(const Frame&)> >& m_loop;
			std::vector< function<void(const Frame&)> >& m_root;
			std::vector< function<void(const Frame&)> >& m_sync;
		};

		typedef dep_stages< function<void(void)> >::null_filter NullFilter;
	}

	// ResolveLoopStage
	void TickScheduler::ResolveLoopStage( void )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|!pool*|!_render") );

		if( !m_reorderLoop )
			return;

		// clear all calls
		m_loopRootCalls.clear();
		m_loopCalls.clear();
		m_loopSyncCalls.clear();

		if( !m_loopStages.traversal(
			_GameLoopFilter( m_loopCalls , m_loopRootCalls , m_loopSyncCalls ) ,
			NullFilter() , NullFilter() ) )
		{
			FATAL_ABORT( _S("Failed to resolve loop stages!\n") );
		}

		m_reorderLoop = false;
	}
	// ResetStages
	void TickScheduler::ResetStages( void )
	{
		m_reorderLoop = true;

		m_loopStages.clear();
		m_loopCalls.clear();
		m_loopSyncCalls.clear();
		m_loopRootCalls.clear();

		// game loop stages
		m_loopStages.insert_stage(	_S("$")		,	_S("")		);
		m_loopStages.insert_stage(	_S("input")	,	_S("$")		);
		m_loopStages.insert_stage(	_S("logic")	,	_S("input")	);
		m_loopStages.insert_stage(	_S("physic"),	_S("logic")	);
		m_loopStages.insert_stage(	_S("scene")	,	_S("physic"));
		m_loopStages.insert_stage(	_S("debug")	,	_S("scene")	);
		m_loopStages.insert_stage(	_S("ui")	,	_S("debug")	);
		m_loopStages.insert_stage(	_S("final")	,	_S("ui")	);
		m_loopStages.insert_stage(	_S("sync")	,	_S("final")	);
	}

} // namespace xeres
